{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c0adef2c",
   "metadata": {},
   "source": [
    "# 비디오(Video) 질의 응답 LLM (Gemini)\n",
    "\n",
    "**주요 흐름**\n",
    "\n",
    "1. `File API`를 사용하여 비디오 파일을 업로드합니다.\n",
    "2. `GenerateContent` 요청을 통해 비디오에 대한 질문을 요청합니다.\n",
    "3. 생성된 응답을 확인합니다.\n",
    "4. 업로드한 Video 파일을 삭제합니다.\n",
    "\n",
    "\n",
    "**중요:** \n",
    "\n",
    "- 본 튜토리얼의 `File API` 는 인증 및 접근을 위해 `API keys`를 사용합니다. \n",
    "\n",
    "- 업로드된 파일은 `API key`의 클라우드 프로젝트와 연결됩니다. \n",
    "\n",
    "다른 `Gemini API` 와 달리, `API key`는 `File API`에 업로드한 데이터에 대한 접근 권한도 부여하므로 `API key`를 안전하게 보관하는 데 특별히 주의해야 합니다.\n",
    "\n",
    "**Reference**\n",
    "\n",
    "- [Gemini API(Cookbook) - Video](https://ai.google.dev/api/rest/v1/models/generateContent#media)\n",
    "\n",
    "**API KEY 발급**\n",
    "\n",
    "- [링크](https://makersuite.google.com/app/apikey?hl=ko) 에서 API KEY를 발급받아주세요.\n",
    "- 사용자의 Google API 키를 환경 변수 `GOOGLE_API_KEY`로 설정합니다.\n",
    "\n",
    "`.env` 파일에 아래와 같이 입력합니다.\n",
    "\n",
    "```\n",
    "GOOGLE_API_KEY=<사용자의 API KEY>\n",
    "```\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f4087686",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "LangSmith 추적을 시작합니다.\n",
      "[프로젝트명]\n",
      "CH04-Models\n"
     ]
    }
   ],
   "source": [
    "# LangSmith 추적 설정\n",
    "# !pip install langchain-teddynote\n",
    "from langchain_teddynote import logging\n",
    "\n",
    "# 프로젝트 이름 입력\n",
    "logging.langsmith(\"CH04-Models\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "d1018ebe",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "LangSmith 추적을 시작합니다.\n",
      "[프로젝트명]\n",
      "CH04-Gemini-Video\n"
     ]
    }
   ],
   "source": [
    "# LangSmith 추적 설정\n",
    "# !pip install langchain-teddynote\n",
    "from langchain_teddynote import logging\n",
    "\n",
    "# 프로젝트 이름 입력\n",
    "logging.langsmith(\"CH04-Gemini-Video\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50776830",
   "metadata": {},
   "source": [
    "## 비디오 업로드\n",
    "\n",
    "`Gemini API` 는 비디오 파일 형식을 직접 수용합니다. \n",
    "\n",
    "**제한사항**\n",
    "\n",
    "- `File API`는 2GB 이하의 파일을 수용하며 프로젝트당 최대 20GB의 파일을 저장할 수 있습니다. \n",
    "- 파일은 2일 동안 유지되며 API에서 **다운로드할 수 없습니다**. \n",
    "\n",
    "본 예제는 테디노트 YouTube 채널에 게시된 비디오를 사용합니다. (다른 비디오로 교체하여 진행해도 좋습니다)\n",
    "\n",
    "- [🧑‍💻 #PDF 전처리 할 수 있는 건 다 해봤음.](https://youtu.be/O3qFWRObAXw)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a347b1ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 파일 다운로드 후 teddynote-sample-video.mp4 파일로 저장\n",
    "!wget \"https://www.dropbox.com/scl/fi/ugue14fyo010jgc7wuh4g/teddynote-sample-video.mp4?rlkey=wcsiktklt7jgoibsluft3m6z9&st=prv4p2uu&dl=1\" -qO teddynote-sample-video.mp4"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "508f1d4f",
   "metadata": {},
   "source": [
    "아래 비디오 파일의 경로를 입력합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "08faf68f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 비디오 파일 이름 지정\n",
    "video_file_name = \"teddynote-sample-video.mp4\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96af89a2",
   "metadata": {},
   "source": [
    "다음으로는 `File API`를 사용하여 비디오 파일을 업로드합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "787df81a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "파일을 업로드 중입니다...\n",
      "업로드 완료: https://generativelanguage.googleapis.com/v1beta/files/b1d0iktswld\n"
     ]
    }
   ],
   "source": [
    "import google.generativeai as genai\n",
    "\n",
    "# 파일 업로드 진행 메시지 출력\n",
    "print(f\"파일을 업로드 중입니다...\")\n",
    "\n",
    "# 파일 업로드 및 파일 객체 반환\n",
    "video_file = genai.upload_file(path=video_file_name)\n",
    "\n",
    "# 업로드 완료 메시지 및 파일 URI 출력\n",
    "print(f\"업로드 완료: {video_file.uri}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "417a1621",
   "metadata": {},
   "source": [
    "파일을 업로드한 후, `files.get` 을 호출하여 API가 파일을 성공적으로 완료되었는지 확인할 수 있습니다. \n",
    "\n",
    "`files.get`은  API 키가 속한 클라우드 프로젝트와 연관된 파일 API에 업로드된 파일을 확인할 수 있게 해줍니다. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "9084e87c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "비디오 업로드 및 전처리가 완료될 때까지 잠시만 기다려주세요...\n",
      "비디오 업로드 및 전처리가 완료될 때까지 잠시만 기다려주세요...\n",
      "비디오 업로드 및 전처리가 완료될 때까지 잠시만 기다려주세요...\n",
      "\n",
      "비디오 처리가 완료되었습니다!\n",
      "이제 대화를 시작할 수 있어요: https://generativelanguage.googleapis.com/v1beta/files/b1d0iktswld\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "\n",
    "# 비디오 파일 처리 상태 확인\n",
    "while video_file.state.name == \"PROCESSING\":\n",
    "    # 처리 완료 대기 메시지 출력\n",
    "    print(\"비디오 업로드 및 전처리가 완료될 때까지 잠시만 기다려주세요...\")\n",
    "    # 10초 대기\n",
    "    time.sleep(10)\n",
    "    # 비디오 파일 상태 갱신\n",
    "    video_file = genai.get_file(video_file.name)\n",
    "\n",
    "# 처리 실패 시 예외 발생\n",
    "if video_file.state.name == \"FAILED\":\n",
    "    raise ValueError(video_file.state.name)\n",
    "\n",
    "# 처리 완료 메시지 출력\n",
    "print(\n",
    "    f\"\\n비디오 처리가 완료되었습니다!\\n이제 대화를 시작할 수 있어요: \" + video_file.uri\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40c3e269",
   "metadata": {},
   "source": [
    "비디오가 업로드된 후, `generate_content` 함수를 사용하여 Video 에 대한 질문을 요청할 수 있습니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "bfe51b71",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "이 영상은 PDF 파일을 파싱하는 방법에 대한 튜토리얼입니다. 랭체인에서 PDF 파일을 파싱할 때 레이아웃 분석을 사용하는 방법과 업스테이지 레이아웃 분석 알고리즘을 사용하는 방법을 설명합니다. 또한 라마 파서를 사용하여 PDF 파일을 파싱하고 이미지, 테이블, 텍스트 등을 추출하는 방법을 보여줍니다. \n",
      " 마지막으로, 랭체인에서 여러 PDF 페이지를 하나의 마크다운 파일로 합쳐서 처리하는 방법을 보여줍니다. \n",
      " \n",
      " 튜토리얼을 통해 PDF 파일을 파싱하는 방법을 배우고 랭체인을 더 효과적으로 사용하는 방법을 익힐 수 있습니다. \n"
     ]
    }
   ],
   "source": [
    "# 프롬프트\n",
    "prompt = \"이 영상에 대해서 짧게 요약해 줄 수 있나요?\"\n",
    "\n",
    "# 모델을 Gemini 1.5 Flash로 설정\n",
    "model = genai.GenerativeModel(model_name=\"models/gemini-1.5-flash\")\n",
    "\n",
    "# LLM 답변 요청\n",
    "response = model.generate_content(\n",
    "    [prompt, video_file], request_options={\"timeout\": 600}\n",
    ")\n",
    "# 결과 출력\n",
    "print(response.text)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a542bbd",
   "metadata": {},
   "source": [
    "아래는 스트림 출력 예제입니다. (`stream=True` 옵션 추가)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "b8c20c5f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Gencon에 대한 언급은 0:27부터 나옵니다. \n",
      "\n",
      "\"오늘 또 이벤트가 있습니다. 젠콘, 젠콘 이벤트 무료로 세 분께 드리는 이벤트 하거든요. 중간에 퀴즈를 낼 건데. 중간에 아무를 제가 말씀드릴 거예요. 아, 라이브에 들으시는 분들은 아무를 쉽게 가실 수 있겠는데 설문에다 작성해주시면 됩니다. 그래서 9월 20일 날 혹시 시간 되시는 분들은 여기 오셔 가지고 들으시면은 또 도움이 될 것 같아요.\" \n"
     ]
    }
   ],
   "source": [
    "# 프롬프트 생성\n",
    "prompt = \"이 영상에서 Gencon 관련 언급한 부분의 시간을 알려주고, 어떤 내용을 말했는지 알려주세요.\"\n",
    "\n",
    "# 모델을 Gemini 1.5 Flash로 설정\n",
    "model = genai.GenerativeModel(model_name=\"models/gemini-1.5-flash\")\n",
    "\n",
    "# LLM 스트림 답변 요청\n",
    "response = model.generate_content(\n",
    "    [prompt, video_file], request_options={\"timeout\": 600}, stream=True\n",
    ")\n",
    "\n",
    "# 생성된 콘텐츠 출력\n",
    "for chunk in response:\n",
    "    print(chunk.text, end=\"\", flush=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c30df5cd",
   "metadata": {},
   "source": [
    "## 파일 삭제\n",
    "\n",
    "파일은 2일 후 자동으로 삭제되거나 `files.delete()`를 사용하여 수동으로 삭제할 수 있습니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "d2871274",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "영상을 삭제했습니다: https://generativelanguage.googleapis.com/v1beta/files/b1d0iktswld\n"
     ]
    }
   ],
   "source": [
    "# 파일 삭제\n",
    "genai.delete_file(video_file.name)\n",
    "\n",
    "# 삭제 완료 메시지 출력\n",
    "print(f\"영상을 삭제했습니다: {video_file.uri}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain-kr-lwwSZlnu-py3.11",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
